Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test shared library on Windows #186

Merged
merged 1 commit into from
Feb 23, 2022
Merged

Test shared library on Windows #186

merged 1 commit into from
Feb 23, 2022

Conversation

Danielius1922
Copy link
Member

No description provided.

@Danielius1922
Copy link
Member Author

Danielius1922 commented Feb 11, 2022

This is POC for solving issue #182. I've created a small shared library (uuid-shared) and test application (uuid-app), uuid-app links with uuid-shared and it compiles successfully on both Linux and Windows.
If the naming and location are fine then we can merge the oc_export.h header. Though going through all header files and adding the annotation will take a while. Maybe we can do it by parts somehow?

@WAvdBeek
Copy link
Contributor

This is POC for solving issue #182. I've created a small shared library (uuid-shared) and test application (uuid-app), uuid-app links with uuid-shared and it compiles successfully on both Linux and Windows. If the naming and location are fine then we can merge the oc_export.h header. Though going through all header files and adding the annotation will take a while. Maybe we can do it by parts somehow?

Note that from python we are just using the oc_python.c file... so if we have an header file for that code, then one can build C# like the python code. C# has a similar interface as python c_types, e.g. add the call format to the dll in the C# code.
https://www.nag.com/IndustryArticles/Calling_C_Library_DLLs_from_C_Sharp.pdf

@Danielius1922
Copy link
Member Author

Danielius1922 commented Feb 11, 2022

Note that from python we are just using the oc_python.c file... so if we have an header file for that code, then one can build C# like the python code. C# has a similar interface as python c_types, e.g. add the call format to the dll in the C# code. https://www.nag.com/IndustryArticles/Calling_C_Library_DLLs_from_C_Sharp.pdf

You mean adding oc_python.h with the OC_API annotation for non-static functions or am I misunderstanding?

@WAvdBeek
Copy link
Contributor

Note that from python we are just using the oc_python.c file... so if we have an header file for that code, then one can build C# like the python code. C# has a similar interface as python c_types, e.g. add the call format to the dll in the C# code. https://www.nag.com/IndustryArticles/Calling_C_Library_DLLs_from_C_Sharp.pdf

You mean adding oc_python.h with the OC_API annotation for non-static functions or am I misunderstanding?

correct. then we can also run the python (client) code on windows. not sure if we want to do the same API as JAVA, that is a considerable amount of work.

@Danielius1922
Copy link
Member Author

Danielius1922 commented Feb 11, 2022

correct. then we can also run the python (client) code on windows. not sure if we want to do the same API as JAVA, that is a considerable amount of work.

Sure, no problem, I can do it, shouldn't take that long to do the Windows part.

include/oc_export.h Outdated Show resolved Hide resolved
CMakeLists.txt Outdated
)

# Python client
if(UNIX OR WIN32)
set(client-python-lib-obj
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I don't think I can reuse the .o files. I need different annotations when I compile for static or shared lib.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Oxymoron79 I guess the solution here is to create shared version of the object libraries (common-obj and common-obj-shared, etc.), since they have differing compile definitions. Though they differ only in the compile definitions, so the CMake code for both targets will be very similar. I should probably create a CMake function that creates a library (both static and shared) target to avoid most of the duplicity.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that really necessary? The point of the object libraries is to generate static and dynamic libraries without the need to recompile the source...

Is your reasoning for this the declspec(dllexport) annotation for Windows? If so, I doubt that different object files are needed, because the user of the DLL has to change the annotation to declspec(dllimport) when linking to the binary (which was compiled with declspec(dllexport)). But my knowledge of Windows development is limited - so take that with a grain of salt 😉

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like avoid the duplicity if possible, but when I compile libiotivity-lite-client-python.so.2.2.5 and examine the public symbols using nm -D command - I see that all global functions from common-obj, tinycbor-master, client-obj and python-obj are public. Thus, adding C_VISIBILITY to the final shared library target had no effect. It seems that I have to compile the .o files with the macro definitions for a shared library, linking is not enough. However, I haven't tried it the other way around, add definitions for shared library to the object library targets. If static library targets won't break then that could be the way.

(OT: iotivity builds by CYGWIN and MINGW seem to be broken, but I think I've found the issue; fun for Friday afternoon :D )

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand https://cmake.org/cmake/help/latest/policy/CMP0063.html correctly, you can set this policy to NEW and then set the C_VISIBILITY on the object library.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(OT: iotivity builds by CYGWIN and MINGW seem to be broken, but I think I've found the issue; fun for Friday afternoon :D )

I came across this issue as well. One of our Windows developers found that adding this:

#ifdef __MINGW32__
#undef interface
#endif

in port/windows/tcpadapter.c and port/windows/network_addresses.c after the include statements fixes the build.

To me, this seems like quite a hack - so I didn't propose the fix upstream. And I also don't know whether CYGWIN and MINGW build are supported by iotivity-lite.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, that looks indeed relevant. Though I still think that this code is problematic:

#ifdef OC_SHARED_LIBRARY
#ifdef OC_SHARED_LIBRARY_EXPORT
#define OC_API OC_EXPORT
#else /* !OC_SHARED_LIBRARY_EXPORT*/
#define OC_API OC_IMPORT
#endif /* OC_SHARED_LIBRARY_EXPORT */
#else  /* !OC_SHARED_LIBRARY */
#define OC_API
#endif /* OC_SHARED_LIBRARY */

I'm reading about dllexport and if I understand it correctly, whatever the compiler does with the attribute is compiled into the .o file. I see no way around the fact that the .o files are different between static and shared libraries. Shared libraries need OC_SHARED_LIBRARY and OC_SHARED_LIBRARY_EXPORT definitions during compilation. The only way this works is by compiling the object files with those definitions and hoping that a static library doesn't have a problem with that.

I think that it is correct to apply the visibility also on static libraries (btw the current visibility default made all symbols globally visible anyways). So, applying the compile definitions to the object library would promote the correct symbol visibility to the static and the shared library, right?

Though I can think of one side effect, if you compile a static lib from such object files and then link that static lib into a dynamic library, I think the symbols from the static lib will be exposed as global symbols in the final shared library files since they will have the export attribute.

Yes, that's true. I found this question (with an answer) also on stackoverflow: https://stackoverflow.com/questions/2222162/how-to-apply-fvisibility-option-to-symbols-in-static-libraries.

Copy link
Member Author

@Danielius1922 Danielius1922 Feb 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, seems like we should be able to get both static and shared libraries from object on Unixes. Not sure what will happen on MSVC, but I guess if my testing app links and runs it should be alright ...?

Though if the definitions will be added to both static and shared libraries I should change the names: OC_SHARED_LIBRARY -> OC_LIBRARY and OC_SHARED_LIBRARY_EXPORT -> OC_LIBRARY_EXPORT.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, seems like we should be able to get both static and shared libraries from object on Unixes. Not sure what will happen on MSVC, but I guess if my testing app links and runs it should be alright ...?

Yes, I think so as well.

Though if the definitions will be added to both static and shared libraries I should change the names: OC_SHARED_LIBRARY -> OC_LIBRARY and OC_SHARED_LIBRARY_EXPORT -> OC_LIBRARY_EXPORT.

Sounds good to me.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to work. Compilation of testing apps that use static lib (uuid-app-static) and shared lib (uuid-app-shared) generated from object lib succeeds on both Linux and Win (see PR jobs). I've also examined uuid-shared with nm -D on Linux and with dumpbin /exports on Win10 locally and the lib only has the manually exposed symbols visible.

Additional notes:

  • CMP0063 is set to NEW by default, so I skipped setting it explicitly
  • When compiling a static lib into a shared lib, gcc (at least my 9.3 version) seems to automatically hide all symbols from static library. So one must add compiler flags to make them visible, however this is not an interesting use case currently, so I didn't pursue it further.

OK, so I'll remove the uuid-* testing stuff and we can start annotating public functions and enabling compilation of DLLs on Windows. @WAvdBeek which functions are considered to be public, only those with headers in include/ folder? Any other? (TBH, in my app that links dynamically with libiotivity-client-server.so, I use functions from all over the place, from api, security, port and util folders at least. But I'll work it out.)

@Danielius1922 Danielius1922 force-pushed the adam/feature/add-exports branch 2 times, most recently from 5cb2977 to 6e9560d Compare February 11, 2022 18:02
include/oc_export.h Outdated Show resolved Hide resolved
include/oc_export.h Outdated Show resolved Hide resolved
@Danielius1922 Danielius1922 force-pushed the adam/feature/add-exports branch 4 times, most recently from 506253e to 4bea4ae Compare February 15, 2022 08:23
@Danielius1922
Copy link
Member Author

(OT: iotivity builds by CYGWIN and MINGW seem to be broken, but I think I've found the issue; fun for Friday afternoon :D )

I came across this issue as well. One of our Windows developers found that adding this:

#ifdef __MINGW32__
#undef interface
#endif

in port/windows/tcpadapter.c and port/windows/network_addresses.c after the include statements fixes the build.

To me, this seems like quite a hack - so I didn't propose the fix upstream. And I also don't know whether CYGWIN and MINGW build are supported by iotivity-lite.

Yes, I spent a good half an hour trying to decipher a weird compilation error on a line with a simple definition of a variable named interface. :D Redefining a common word like interface using a macro seems like a bad idea, but I guess it was necessary for some reason. I would just rename the variables (intf or iface or whatever).

I think at least MINGW was previously supported or intended to be supported, see the cmake-windows workflow:

#- {
# name: "Windows Latest MinGW", artifact: "Windows-MinGW.tar.xz",
# os: windows-latest,
# build_type: "Release", cc: "gcc", cxx: "g++"
# }

I had to do some additional updates to the CMakeLists.txt (if(UNIX) is true in both CYGWIN and MINGW), but after that I was able to make the compilation succeed on both platforms. I think we can get support for both for pretty cheap.

@WAvdBeek
Copy link
Contributor

(OT: iotivity builds by CYGWIN and MINGW seem to be broken, but I think I've found the issue; fun for Friday afternoon :D )

I came across this issue as well. One of our Windows developers found that adding this:

#ifdef __MINGW32__
#undef interface
#endif

in port/windows/tcpadapter.c and port/windows/network_addresses.c after the include statements fixes the build.
To me, this seems like quite a hack - so I didn't propose the fix upstream. And I also don't know whether CYGWIN and MINGW build are supported by iotivity-lite.

Yes, I spent a good half an hour trying to decipher a weird compilation error on a line with a simple definition of a variable named interface. :D Redefining a common word like interface using a macro seems like a bad idea, but I guess it was necessary for some reason. I would just rename the variables (intf or iface or whatever).

I think at least MINGW was previously supported or intended to be supported, see the cmake-windows workflow:

#- {
# name: "Windows Latest MinGW", artifact: "Windows-MinGW.tar.xz",
# os: windows-latest,
# build_type: "Release", cc: "gcc", cxx: "g++"
# }

I had to do some additional updates to the CMakeLists.txt (if(UNIX) is true in both CYGWIN and MINGW), but after that I was able to make the compilation succeed on both platforms. I think we can get support for both for pretty cheap.

nope, this yaml file was just copied from somewhere else and I made it work for visual studio only.
e.g. as precursor of deprecating the visual studio file under port/windows.
maybe the cygwin worked when using the linux port (although I never tested that myself).

@Danielius1922
Copy link
Member Author

nope, this yaml file was just copied from somewhere else and I made it work for visual studio only. e.g. as precursor of deprecating the visual studio file under port/windows. maybe the cygwin worked when using the linux port (although I never tested that myself).

I see, we can get Mingw and Cygwin with some minor code and CMake changes. Though the CMake doesn't run tests, so successful compilation might not mean that everything is in order.

@Danielius1922 Danielius1922 force-pushed the adam/feature/add-exports branch from 4bea4ae to 2473c45 Compare February 18, 2022 09:55
@WAvdBeek
Copy link
Contributor

what needs to be done before merge?

@Danielius1922
Copy link
Member Author

what needs to be done before merge?

Well, I've created testing apps (uuid-app, uuid-shared-app) and libraries (uuid-obj, uuid-static, uuid-shared) and I don't think we should merge those into master. However, those are an example of how to do it, so we should create at least one library target with static and shared versions that work. I guess the client library is the smallest? We need to annotate all public functions with the OC_API macro, I can start doing it, but I'm not sure which functions exactly are considered public. Only those in the include/ folder? Any others?

@WAvdBeek
Copy link
Contributor

what needs to be done before merge?

Well, I've created testing apps (uuid-app, uuid-shared-app) and libraries (uuid-obj, uuid-static, uuid-shared) and I don't think we should merge those into master. However, those are an example of how to do it, so we should create at least one library target with static and shared versions that work. I guess the client library is the smallest? We need to annotate all public functions with the OC_API macro, I can start doing it, but I'm not sure which functions exactly are considered public. Only those in the include/ folder? Any others?

oc_python.h, so that we can use the python code on windows. For me that would be enough for a merge.

@Danielius1922 Danielius1922 force-pushed the adam/feature/add-exports branch from 2473c45 to e734521 Compare February 22, 2022 16:39
@Danielius1922 Danielius1922 force-pushed the adam/feature/add-exports branch from e734521 to 3cde7a0 Compare February 22, 2022 16:42
@Danielius1922
Copy link
Member Author

oc_python.h, so that we can use the python code on windows. For me that would be enough for a merge.

I've removed the testing apps and iotivity-lite-client-python.dll compiled on Windows / libiotivity-lite-client-python.so on Linux, though on Linux symbols from common-obj, tinycbor-master, client-obj and mbedtls are exported as part of the lib. I think I can hide all symbols from targets mbedtls and tinycbor-master, those are third-party libs, no reason to expose their symbols as part of any iotivity library. For targets common-obj and client-obj I think some functions should be exported.

@WAvdBeek
Copy link
Contributor

oc_python.h, so that we can use the python code on windows. For me that would be enough for a merge.

I've removed the testing apps and iotivity-lite-client-python.dll compiled on Windows / libiotivity-lite-client-python.so on Linux, though on Linux symbols from common-obj, tinycbor-master, client-obj and mbedtls are exported as part of the lib. I think I can hide all symbols from targets mbedtls and tinycbor-master, those are third-party libs, no reason to expose their symbols as part of any iotivity library. For targets common-obj and client-obj I think some functions should be exported.

not sure if it is an actual issue that those symbols are exported. those symbols are used internally in the shared object/dll anyway.

@Danielius1922
Copy link
Member Author

not sure if it is an actual issue that those symbols are exported. those symbols are used internally in the shared object/dll anyway.

Yes, that's not a problem that the shared library uses those symbols internally, the issue is that the shared library exports those symbols (at least on Unixes). That is they are available to a binary that links with the shared library.
The default behavior is different on MSVC and on GCC-compatible compilers (gcc, clang, mingw, cygwin). MSVC hides symbols by default, so global function from those mentioned targets are not visible to the clients of the .dll. GCC on other hand exposes symbols by default, so all global functions from the mentioned targets are visible to the clients of the .so.

Take the client-python-shared which is now built by this CMake on both Windows and on Unix:

  • for Windows (with MSVC): the .dll only exposes functions annotated in the oc_python.h
  • for Unix: the .so exposes functions from oc_python.h, but also all global functions from common-obj, tinycbor-master,
    client-obj, mbedtls.

I think we should have same or at least very similar exposed API on all platforms, so additional work is needed. But oc_python.c stuff is workable now, so I suggest we merge this PR and the remaining work will be done later.

@Danielius1922 Danielius1922 marked this pull request as ready for review February 23, 2022 09:58
@WAvdBeek WAvdBeek merged commit 3f760f2 into master Feb 23, 2022
@github-actions github-actions bot locked and limited conversation to collaborators Feb 23, 2022
@Danielius1922 Danielius1922 deleted the adam/feature/add-exports branch September 8, 2022 10:13
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants